package com.ep.thirdPlatformProject.fadada.service.impl;


import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.ep.thirdPlatformProject.fadada.service.FaDaDaService;
import com.ep.thirdPlatformProject.system.config.fadada.FaDaDaProperties;
import com.fasc.open.api.bean.base.BaseRes;
import com.fasc.open.api.bean.common.OpenId;
import com.fasc.open.api.enums.corp.CorpIdentTypeEnum;
import com.fasc.open.api.enums.signtask.SignTaskStatusEnum;
import com.fasc.open.api.exception.ApiException;
import com.fasc.open.api.v5_1.client.*;
import com.fasc.open.api.v5_1.req.signtask.*;
import com.fasc.open.api.v5_1.res.signtask.GetSignTaskListRes;
import com.fasc.open.api.v5_1.res.signtask.OwnerDownloadUrlRes;
import com.fasc.open.api.v5_1.res.signtask.SignTaskInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tika.Tika;
import org.apache.tika.mime.MimeTypes;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import java.io.FileOutputStream;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.*;
import java.util.concurrent.CompletableFuture;

@Service
@Slf4j
@RequiredArgsConstructor
public class FaDaDaServiceImpl implements FaDaDaService {

    /**
     * 基础属性
     */
    private final OpenApiClient openApiClient;

    /**
     * 属性
     */
    private final FaDaDaProperties properties;

    /**
     * 执行器
     */
    private final ThreadPoolTaskExecutor executor;


    /**
     * 获取下载后返回地址
     */
    @Override
    public List<String> getStorePathList() {
        try {
            ServiceClient serviceClient = new ServiceClient(openApiClient);
            var baseRes = serviceClient.getAccessToken();
            if (baseRes.isSuccess()) {
                SignTaskClient signTaskClient = new SignTaskClient(openApiClient);
                String accessToken = baseRes.getData().getAccessToken();
                List<SignTaskInfo> taskList = getSignTaskByCondition(signTaskClient, accessToken);
                List<CompletableFuture<String>> futureList = taskList.stream().parallel().map(
                        item -> CompletableFuture.supplyAsync(
                                () -> getOwnerDownloadUrlReq(signTaskClient, accessToken, item)
                                , executor
                        ).handle((result, throwable) -> {
                            if (throwable != null) {
                                System.out.println("任务异常! e=" + throwable + ", " + new Date());
                                log.error("任务异常! e={}, {}", throwable, new Date(), throwable);
                            } else {
                                return result;
                            }
                            return item.getSignTaskId() + "处理异常";
                        })
                ).toList();
                List<String> downloadList = futureList.stream().map(CompletableFuture::join).toList();
                System.out.println("downloadList = " + downloadList);
                return downloadList;
            }


        } catch (Exception e) {
            log.error("请求发送失败了，原因是：{}", e.getMessage(), e);

        }
        return null;
    }

    /**
     * 构建所有者id
     *
     * @return {@link OpenId }
     */
    private OpenId buildOwnerId() {
        OpenId openId = new OpenId();
        openId.setIdType(CorpIdentTypeEnum.CORP.getCode());
        openId.setOpenId(properties.getOpenCorpId());
        return openId;
    }

    /**
     * 按条件获取签名任务
     *
     * @param signTaskClient 签名任务客户端
     * @param accessToken    访问令牌
     * @return {@link List }<{@link SignTaskInfo }>
     * @throws ApiException api例外
     */
    private List<SignTaskInfo> getSignTaskByCondition(SignTaskClient signTaskClient, String accessToken) throws ApiException {
        Date startDate = DateUtil.beginOfMonth(Calendar.getInstance()).getTime();
        Date endDate = DateUtil.endOfDay(Calendar.getInstance()).getTime();
        long start = startDate.getTime();
        long end = endDate.getTime();

        GetOwnerSignTaskListReq req = new GetOwnerSignTaskListReq();
        req.setOwnerId(buildOwnerId());
        SignTaskListFilter filter = new SignTaskListFilter();
        if (StrUtil.isEmpty(String.valueOf(start)) && StrUtil.isEmpty(String.valueOf(end))) {
            throw new ApiException("查询任务列表的开始和结束时间不能同时为空！");
        }
        filter.setFinishTimeFrom(String.valueOf(start));
        filter.setFinishTimeTo(String.valueOf(end));
        List<String> list = new ArrayList<>();
        String complete = SignTaskStatusEnum.SIGN_COMPLETED.getCode();
        String finished = SignTaskStatusEnum.TASK_FINISHED.getCode();
        list.add(complete);
        list.add(finished);
        filter.setSignTaskStatus(list);
        req.setListFilter(filter);
        req.setAccessToken(accessToken);
        BaseRes<GetSignTaskListRes> ownerList = signTaskClient.getOwnerList(req);
        GetSignTaskListRes data = ownerList.getData();


        log.info("当前查询条件为：{开始时间:{}, 结束时间:{},筛选条件:{},{}}", DateUtil.format(startDate, DatePattern.NORM_DATETIME_FORMATTER),
                DateUtil.format(endDate, DatePattern.NORM_DATETIME_FORMATTER), complete, finished);
        if (data != null && !data.getSignTasks().isEmpty()) {
            return data.getSignTasks();
        } else {
            throw new ApiException("查询结果为空！");
        }
    }

    /**
     * 构建请求
     *
     * @param accessToken 访问令牌
     * @return {@link GetOwnerDownloadUrlReq }
     */
    private String getOwnerDownloadUrlReq(SignTaskClient signTaskClient, String accessToken, SignTaskInfo info) {
        try {
            GetOwnerDownloadUrlReq req = new GetOwnerDownloadUrlReq();


            //（可选）企业组织类型CorpIdentTypeEnum
            req.setOwnerId(buildOwnerId());
            req.setSignTaskId(info.getSignTaskId());
            req.setAccessToken(accessToken);

            BaseRes<OwnerDownloadUrlRes> res = signTaskClient.getOwnerDownloadUrl(req);
            if (res.getData() != null) {
                OwnerDownloadUrlRes data = res.getData();
                String urlStr = data.getDownloadUrl();
                log.info("当前taskId：{},对应的下载文件编号为：{}", info.getSignTaskId(), urlStr);
                // 上传到minio中
                String location = saveToLocal(urlStr, info);
                log.info("当前存储本地位置为：{}", location);
                return location;
            } else {
                throw new ApiException(res.getMsg());
            }
        } catch (Exception e) {
            log.error("{}，获取下载链接失败,原因是{}", info.getSignTaskId(), e.getMessage());
            return null;
        }

    }

    private String saveToLocal(String urlStr, SignTaskInfo info) {
        ReadableByteChannel rbc = null;
        FileOutputStream fos = null;
        try {
            URL url  = new URL(urlStr);
            String contentType = new Tika().detect(url.openStream());
            String extension = MimeTypes.getDefaultMimeTypes().forName(contentType).getExtension();
            rbc = Channels.newChannel(url.openStream());

            String local =  info.getSignTaskSubject() + info.getSignTaskId() + extension;
            String format = DateUtil.format(DateUtil.date(), DatePattern.NORM_DATE_PATTERN);
            boolean exist = FileUtil.exist(properties.getBaseDir()+format);
            String dirPath =  properties.getBaseDir() + format;;
            if (!exist){
                FileUtil.mkdir(dirPath);
            }
            fos = new FileOutputStream(dirPath+"//"+local);
            fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
            return dirPath+"//"+local;
        } catch (Exception e) {
            log.error("下载文件失败！");
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (Exception e) {
                    log.error("fos关闭失败");
                }
            }
            if (rbc != null) {
                try {
                    rbc.close();
                } catch (Exception e) {
                    log.error("rbc关闭失败");
                }
            }
        }
        return "";
    }


}
